<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>力学导向图（力学）</title>
</head>
<body>
    <!-- SVG -->
    <h1>SVG</h1>
    <div class="svgBox">
    </div>
    <script src="../node_modules/d3/dist/d3.js" charset="utf-8"></script>
    <script type="text/javascript">
        // 准备画布
        let boxWidth = 400;     // 画布宽
        let boxHeight = 400;    // 画布高
        let svg = d3.select('.svgBox')    // 选择文档中的div.svgBox元素
            .append('svg')      // 添加一个svg元素
            .attr('width', boxWidth)    // 设置宽度
            .attr('height', boxHeight)    // 设置高度
            .style('background-color', '#eee')
            .style('border', '1px solid black');

        // 节点（nodes）和连线（edges）的数组，节点是一些城市名，连线的两端是节点的序号（序号从 0 开始）。
        let nodes = [ { name: "桂林" }, { name: "广州" },
            { name: "厦门" }, { name: "杭州" },
            { name: "上海" }, { name: "青岛" },
            { name: "天津" } ];

        let edges = [ { source : 0 , target: 1 } , { source : 0 , target: 2 } ,
            { source : 0 , target: 3 } , { source : 1 , target: 4 } ,
            { source : 2 , target: 3 } , { source : 3 , target: 4 } ,
            { source : 1 , target: 5 } , { source : 1 , target: 6 } ];

        // 数据转换
        // 注意：d3.js v3 与 v4/v5 写法有比较大的改变。
        let force = d3.forceSimulation(nodes) //指定节点数组
            .force("charge", d3.forceManyBody())
            //指定连线数组
            .force('links',
                d3.forceLink(edges)
                    /*
                    * 指定结点连接线的距离，默认为20。我们这里定义为：100
                    * 如果距离是一个常数，那么各连接线的长度总是固定的；
                    * 如果是一个函数，那么这个函数是作用于各连接线（ source , target ）的。
                    * */
                    .distance(100))
            // 定位中心的坐标
            .force("center", d3.forceCenter(boxWidth / 2, boxWidth / 2));

        /*每个 node 必须是一个对象类型，下面的几个属性将会被仿真系统添加:

            > index - 节点在 nodes 数组中的索引
            > x - 节点当前的 x-坐标
            > y - 节点当前的 y-坐标
            > vx - 节点当前的 x-方向速度
            > vy - 节点当前的 y-方向速度
        */
        // console.log(nodes);
        // console.log(edges);

        // 绘制
        /*
        *   分别绘制三种图形元素：
        *       > line，线段，表示连线。
        *       > circle，圆，表示节点。
        *       > text，文字，描述节点。
        * */
        // 分组
        let g = svg.append("g");

        // 1、添加连线
        let svg_edges = g.append("g").selectAll("line")
            .data(edges)
            .enter()
            .append("line")
            // .attr('x1', function(d) {return d.source.x;})
            // .attr('y1', function(d) {return d.source.y;})
            // .attr('x2', function(d) {return d.target.x;})
            // .attr('y2', function(d) {return d.target.y;})
            .style("stroke", "red")
            .style("stroke-width", 1);

        let color = d3.scaleOrdinal(d3.schemeCategory10);   //有十种颜色的颜色比例尺

        // 2、添加节点
        let svg_nodes = g.append("g").selectAll("circle")
            .data(nodes)
            .enter()
            .append("circle")
            .attr("r", 20)
            // .attr("cx",function(d){ return d.x; })
            // .attr("cy",function(d){ return d.y; })
            .style("fill",function(d,i){
                return color(i);
            })
            // 使得节点能够拖动
            .call(d3.drag()
                .on("start", dragStarted)
                .on("drag", dragged)
                .on("end", dragEnded)
            );

        // 添加描述节点的文字
        let svg_texts = g.append("g").selectAll("text")
            .data(nodes)
            .enter()
            .append("text")
            .style("fill", "black")
            .attr("dx", 20)
            .attr("dy", 8)
            // .attr("x", function(d){ return d.x; })
            // .attr("y", function(d){ return d.y; })
            .text(function(d){
                return d.name;
            });

        // 导向图布局 force 有一个事件 tick，每进行到一个时刻，都要调用它，更新的内容就写在它的监听器里就好。
        force.on("tick", function(){ //对于每一个时间间隔
            //更新连线坐标
            svg_edges.attr("x1",function(d){ return d.source.x; })
                .attr("y1",function(d){ return d.source.y; })
                .attr("x2",function(d){ return d.target.x; })
                .attr("y2",function(d){ return d.target.y; });

            //更新节点坐标
            svg_nodes.attr("cx",function(d){ return d.x; })
                .attr("cy",function(d){ return d.y; });

            //更新文字坐标
            svg_texts.attr("x", function(d){ return d.x; })
                .attr("y", function(d){ return d.y; });
        });

        // 拖拽
        function dragStarted(d) {
            if (!d3.event.active) {
                force.alphaTarget(0.3).restart();
            }
            d.fx = d.x;
            d.fy = d.y;
        }

        function dragged(d) {
            d.fx = d3.event.x;
            d.fy = d3.event.y;
        }

        function dragEnded(d) {
            if (!d3.event.active) {
                force.alphaTarget(0);
            }
            d.fx = null;
            d.fy = null;
        }
    </script>
</body>
</html>